package com.shiro.realm;

import com.shiro.config.ShiroConfig;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.Realm;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 自定义Realm，用来实现用户的认证和授权
 * Realm：父类抽象类
 */
public class MyRealm implements Realm {

  @Autowired
  ShiroConfig shiroConfig;

  /**
   * 返回分配给此Realm的（应用程序唯一的）名称。为单个应用程序配置的所有领域都必须具有唯一的名称。
   * 返回值：
   * 分配给此Realm的（应用程序唯一的）名称。
   */
  @Override
  public String getName() {
    return "shirodemo";
  }

  /**
   * 用来验证token类型
   *
   * @param authenticationToken the AuthenticationToken submitted for the authentication attempt
   * @return
   */
  @Override
  public boolean supports(AuthenticationToken authenticationToken) {
    // 此处可以判断token类型是否支持，测试默认固定返回true
    return true;
  }

  @Override
  public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    //将AuthenticationToken强转成UsernamePasswordToken 这样获取账号和密码更加的方便
    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;

    //获取用户在浏览器中输入的账号
    String userName = token.getUsername();

    //  liox:20230129  此处实现业务逻辑
    //认证账号,正常情况我们需要这里从数据库中获取账号的信息，以及其他关键数据，例如账号是否被冻结等等
    String dbUserName = userName;

    if (!"admin".equals(dbUserName) && !"zhangsan".equals(dbUserName)) {//判断用户账号是否正确
      throw new UnknownAccountException("账号错误");
    }
    if ("zhangsan".equals(userName)) {
      throw new LockedAccountException("账号被锁定");
    }
    //定义一个密码(这个密码应该来自数据库)
    String dbpassword = "a18d2133f593d7b0e3ed488560404083";

    //获取用户在浏览器中输入的密码
    String userPass = String.valueOf(token.getPassword());

    // MD5+随机盐加密+散列1024   配置文件中 "1q2w3e"，实际应该自动生成并存库
    Md5Hash md5Hash03 = new Md5Hash(userPass, shiroConfig.getSalt(), 1024);

    if (!dbpassword.equals(md5Hash03.toHex())) {
      throw new IncorrectCredentialsException("密码错误");
    }

    /**
     * 创建密码认证对象，由Shiro自动认证密码
     * 参数1  数据库中的账号（页面账号也可）
     * 参数2  数据库中的密码
     * 参数3  当前Relam的名字
     * 如果密码认证成功，会返回一个用户身份对象；如果密码验证失败则抛出异常
     */
    //认证密码是否正确
    return new SimpleAuthenticationInfo(dbUserName, dbpassword, getName());
  }
}
